package redis

import (
	"context"
	"fmt"
	"time"

	"github.com/redis/go-redis/v9"
)

type Client struct {
	client *redis.Client
}

// NewClient 创建Redis客户端
func NewClient(addr string, password string, db int, poolSize int) *Client {
	rdb := redis.NewClient(&redis.Options{
		Addr:     addr,
		Password: password,
		DB:       db,
		PoolSize: poolSize,
	})

	return &Client{client: rdb}
}

// Ping 检查Redis连接
func (c *Client) Ping(ctx context.Context) error {
	_, err := c.client.Ping(ctx).Result()
	return err
}

// Close 关闭Redis连接
func (c *Client) Close() error {
	return c.client.Close()
}

// GetClient 获取原始Redis客户端
func (c *Client) GetClient() *redis.Client {
	return c.client
}

// VideoSetOperations 视频集合操作接口
type VideoSetOperations interface {
	// AddVideo 添加视频到集合
	AddVideo(ctx context.Context, key string, filename string) error

	// AddVideos 批量添加视频到集合
	AddVideos(ctx context.Context, key string, filenames []string) (int64, error)

	// GetRandomVideos 从集合中随机获取指定数量的视频
	GetRandomVideos(ctx context.Context, key string, count int) ([]string, error)

	// RemoveVideo 从集合中移除视频
	RemoveVideo(ctx context.Context, key string, filename string) error

	// GetVideoCount 获取集合中的视频数量
	GetVideoCount(ctx context.Context, key string) (int64, error)

	// ClearVideos 清空视频集合
	ClearVideos(ctx context.Context, key string) error

	// SetExpiration 设置集合过期时间
	SetExpiration(ctx context.Context, key string, expiration time.Duration) error

	// IsVideoExists 检查视频是否存在于集合中
	IsVideoExists(ctx context.Context, key string, filename string) (bool, error)
}

// AddVideo 添加视频到集合
func (c *Client) AddVideo(ctx context.Context, key string, filename string) error {
	return c.client.SAdd(ctx, key, filename).Err()
}

// AddVideos 批量添加视频到集合
func (c *Client) AddVideos(ctx context.Context, key string, filenames []string) (int64, error) {
	if len(filenames) == 0 {
		return 0, nil
	}

	// 使用pipeline提高性能
	pipe := c.client.Pipeline()
	for _, filename := range filenames {
		pipe.SAdd(ctx, key, filename)
	}

	_, err := pipe.Exec(ctx)
	if err != nil {
		return 0, err
	}

	return int64(len(filenames)), nil
}

// GetRandomVideos 从集合中随机获取指定数量的视频
func (c *Client) GetRandomVideos(ctx context.Context, key string, count int) ([]string, error) {
	if count <= 0 {
		return []string{}, nil
	}

	// 获取集合大小
	setSize, err := c.client.SCard(ctx, key).Result()
	if err != nil {
		return nil, fmt.Errorf("获取集合大小失败: %w", err)
	}

	if setSize == 0 {
		return []string{}, nil
	}

	// 如果请求数量大于集合大小，返回所有成员
	if int64(count) > setSize {
		count = int(setSize)
	}

	// 使用SRANDMEMBER获取随机成员
	result, err := c.client.SRandMemberN(ctx, key, int64(count)).Result()
	if err != nil {
		return nil, fmt.Errorf("获取随机成员失败: %w", err)
	}

	return result, nil
}

// RemoveVideo 从集合中移除视频
func (c *Client) RemoveVideo(ctx context.Context, key string, filename string) error {
	return c.client.SRem(ctx, key, filename).Err()
}

// GetVideoCount 获取集合中的视频数量
func (c *Client) GetVideoCount(ctx context.Context, key string) (int64, error) {
	return c.client.SCard(ctx, key).Result()
}

// ClearVideos 清空视频集合
func (c *Client) ClearVideos(ctx context.Context, key string) error {
	return c.client.Del(ctx, key).Err()
}

// SetExpiration 设置集合过期时间
func (c *Client) SetExpiration(ctx context.Context, key string, expiration time.Duration) error {
	return c.client.Expire(ctx, key, expiration).Err()
}

// IsVideoExists 检查视频是否存在于集合中
func (c *Client) IsVideoExists(ctx context.Context, key string, filename string) (bool, error) {
	return c.client.SIsMember(ctx, key, filename).Result()
}

// BatchOperations 批量操作接口
type BatchOperations interface {
	// BatchAddVideosWithRetry 批量添加视频，支持重试机制
	BatchAddVideosWithRetry(ctx context.Context, key string, filenames []string, maxRetries int) (int64, error)

	// GetAndRemoveRandomVideos 获取并移除随机视频（用于原子操作）
	GetAndRemoveRandomVideos(ctx context.Context, key string, count int) ([]string, error)
}

// BatchAddVideosWithRetry 批量添加视频，支持重试机制
func (c *Client) BatchAddVideosWithRetry(ctx context.Context, key string, filenames []string, maxRetries int) (int64, error) {
	if len(filenames) == 0 {
		return 0, nil
	}

	var added int64
	var lastErr error

	for attempt := 0; attempt <= maxRetries; attempt++ {
		select {
		case <-ctx.Done():
			return added, ctx.Err()
		default:
		}

		result, err := c.AddVideos(ctx, key, filenames)
		if err == nil {
			return result, nil
		}

		lastErr = err
		if attempt < maxRetries {
			// 指数退避重试
			backoff := time.Duration(attempt+1) * 100 * time.Millisecond
			time.Sleep(backoff)
		}
	}

	return added, fmt.Errorf("添加视频失败，重试%d次后仍失败: %w", maxRetries, lastErr)
}

// GetAndRemoveRandomVideos 获取并移除随机视频（用于原子操作）
// GetAndRemoveRandomVideos 使用 Lua 脚本获取并移除随机视频
func (c *Client) GetAndRemoveRandomVideos(ctx context.Context, key string, count int) ([]string, error) {
	// Lua 脚本，原子获取并移除随机视频
	luaScript := `
		local members = redis.call('SRANDMEMBER', KEYS[1], ARGV[1])
		if #members > 0 then
			redis.call('SREM', KEYS[1], unpack(members))
		end
		return members
	`

	// 使用 Lua 脚本执行 Redis 操作
	resp, err := c.client.Eval(ctx, luaScript, []string{key}, count).Result()
	if err != nil {
		return nil, fmt.Errorf("原子操作失败: %w", err)
	}

	// 将返回的结果转换为字符串切片
	members, ok := resp.([]interface{})
	if !ok {
		return nil, fmt.Errorf("无法转换 Lua 脚本结果")
	}

	// 转换为字符串切片
	var result []string
	for _, member := range members {
		result = append(result, member.(string))
	}

	return result, nil
}

// HealthCheck 健康检查
type HealthCheck struct {
	client *Client
}

// NewHealthCheck 创建健康检查实例
func NewHealthCheck(client *Client) *HealthCheck {
	return &HealthCheck{client: client}
}

// Check 检查Redis健康状态
func (h *HealthCheck) Check(ctx context.Context) error {
	return h.client.Ping(ctx)
}

// GetStatus 获取Redis状态信息
func (h *HealthCheck) GetStatus(ctx context.Context) (map[string]interface{}, error) {
	info := make(map[string]interface{})

	// 检查连接
	if err := h.Check(ctx); err != nil {
		info["status"] = "unhealthy"
		info["error"] = err.Error()
		return info, err
	}

	info["status"] = "healthy"

	// 获取Redis信息
	redisInfo, err := h.client.GetClient().Info(ctx).Result()
	if err == nil {
		info["info"] = redisInfo
	}

	return info, nil
}
